package calao.plan;

import calao.plan.client.Task;
import calao.storage.Field;
import calao.storage.Record;
import calao.storage.Sequence;
import calao.storage.Table;
import calao.util.Arrays;
import calao.util.Bits;

public class TaskDB {
	
	public static final int FIXED_START_FLAG = 1;
	public static final int FIXED_END_FLAG = 2;
	public static final int FINALIZED_FLAG = 4;

	public static final Table TABLE = new Table("calao.plan.Task");
	
	static final Sequence ID_SEQUENCE = new Sequence("calao.plan.TaskId");

	public static final Field<Integer> ID = 
		TABLE.createField("id", Field.INTEGER);	

	public static final Field<Integer> TYPE = 
		TABLE.createField("type", Field.INTEGER);	

	/** Weeks that contain all or part of the task.
	 */  
	public static final Field<Integer> WEEK = 
		TABLE.createField("week", Field.INTEGER, true);		
	
	/** Pool that contains the task.
	 */
	public static final Field<Integer> POOL = 
		TABLE.createField("pool", Field.INTEGER, true);	

	public static final Field<Integer> RESOURCE = 
		TABLE.createField("resource", Field.INTEGER);	

	public static final Field<Long> START = 
		TABLE.createField("start", Field.LONG);	

	public static final Field<Long> END = 
		TABLE.createField("end", Field.LONG);	

	public static final Field<Integer> DEPENDENCIES = 
		TABLE.createField("dependencies", Field.INTEGER, true);	

	public static final Field<Integer> PRIORITY = 
		TABLE.createField("priority", Field.INTEGER);	

	public static final Field<Integer> FLAGS = 
		TABLE.createField("flags", Field.INTEGER);	

	public static final Field<Integer> WORK_TIME = 
		TABLE.createField("workTime", Field.INTEGER);
	
	public static final Field<Task> TYPE_DATA = 
		TABLE.createField("typeData", Task.class);

	static {
		TABLE.createPrimaryKeyIndex(ID);
		// optimize searches on week or week+resource
		TABLE.createIndex(WEEK, RESOURCE);
		// allows fast search on pool or pool+resource
		TABLE.createIndex(POOL, RESOURCE);
	}
	
	public static boolean setStart(Record rec, long time)
	{
		Long pval = rec.get(START);
		if (pval==null? time==0 : pval.equals(time)) return false;
		rec.set(START, time);
		return true;
	}
	
	public static boolean setEnd(Record rec, long time)
	{
		try {
			Long pval = rec.get(END);
			if (pval==null? time==0 : pval.equals(time)) return false;
			rec.set(END, time);
			return true;
		}
		finally {
			updateWeek(rec);			
		}
	}
	
	public static boolean setResource(Record rec, int id)
	{
		Integer pval = rec.get(RESOURCE);
		if (pval==null? id==0 : pval.equals(id)) return false;
		rec.set(RESOURCE, id);
		return true;
	}

	public static boolean setPriority(Record rec, int priority)
	{
		Integer pval = rec.get(PRIORITY);
		if (pval==null? priority==0 : pval.equals(priority)) return false;
		rec.set(PRIORITY, priority);
		return true;
	}

	public static boolean setWorktime(Record rec, int minutes)
	{
		Integer pval = rec.get(WORK_TIME);
		if (pval==null? minutes==0 : pval.equals(minutes)) return false;
		rec.set(WORK_TIME, minutes);
		return true;
	}

	public static boolean setFlag(Record rec, int flag)
	{
		int flags = rec.getInt(FLAGS, 0);
		if (Bits.hasBits(flags, flag)) return false;
		rec.set(FLAGS, Bits.setBits(flags, flag));
		return true;
	}
	
	public static boolean clearFlag(Record rec, int flag)
	{
		int flags = rec.getInt(FLAGS, 0);
		if (!Bits.hasBits(flags, flag)) return false;
		rec.set(FLAGS, Bits.clearBits(flags, flag));
		return true;
	}

	public static boolean hasFlag(Record rec, int flag)
	{
		return Bits.hasBits(rec.getInt(FLAGS, 0), flag);
	}

	public static boolean setDependencies(Record rec, int[] deps)
	{
		Integer[] oldDeps = rec.getAll(DEPENDENCIES);
		boolean modified = oldDeps.length!=deps.length;
		if (!modified) for (int dep : deps) {
			if (!Arrays.contains(oldDeps, dep)) {
				modified = true;
				break;
			}
		}
		if (!modified) return false;
		rec.set(DEPENDENCIES, deps);
		return true;
	}
	
	public static boolean inPool(Record rec, int pool)
	{
		// TODO add support for multiple pools (user-defined pools)
		return rec.getInt(TaskDB.POOL)==pool;
	}
	
	public static void removePool(Record rec, int pool)
	{
		// TODO add support for multiple pools (user-defined pools)
		final int[] pools = {};
		rec.set(POOL, pools);
	}
	
	public static void addPool(Record rec, int pool)
	{
		// TODO add support for multiple pools (user-defined pools)
		rec.set(POOL, pool);
	}

	public static Integer[] getPools(Record rec)
	{
		return rec.getAll(POOL);
	}

	private static void updateWeek(Record rec)
	{
		Long start = rec.get(START);
		Long end = rec.get(END);
		if (start!=null && end!=null) {
			rec.set(WEEK, Weeks.getPeriodWeeks(start, end));
		}
	}
	
}
